package org.hsc.proxy;
/*
 * ReliableUDPClient.java
 *
 * Created on December 23, 2007
 *
 * Name:        Moty Michaely   |   Alon Halfon
 * ID:          043413087       |   300545969
 */

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;

/**
 * A reliable udp client.
 * 
 * @author Alon Halfon
 */
public class ReliableUDPClient {
    
    private static final int maxRetries=6;
    /*
     * the socket we will use in this connection
     */
    protected DatagramSocket socket=null;
    /*
     * unique message identifier (LAST MESSAGE)
     */
    private int messageID=0;
    /*
     * address of the messages target
     */
    protected InetSocketAddress address=null;
    /*
     * port
     */
    protected int port;
     /*
     * after timeout milisecond of on response - server dead
     */
    private int timeout;
    
    public DatagramSocket getSocket() {
        return this.socket;
    }
    
    public int getPort() {
        return this.port;
    }
    
    
    ReliableUDPClient(){
    }
    /*
     * c-tor for the mediator port and timeout interval. //in use by the mediator
     */
    ReliableUDPClient (int port,int timeout) throws SocketException{
        socket=new DatagramSocket(port);
        this.timeout=timeout;
        this.port=port;
        
      
    }    
    /*
     * c-tor for the manager (IP is known) with timeout interval.
     */   
    ReliableUDPClient (int port,InetSocketAddress address,int timeout) throws SocketException{
        socket=new DatagramSocket();
        this.address=address;
        this.timeout=timeout;
        this.port=port;

    }
    /*
     * c-tor for the mediator (without timeout) (NOT IN USE- Address is unknown)
     */
    ReliableUDPClient (int port,InetSocketAddress address) throws SocketException{
        this(port, address, -1);
        
    }
    /*
     * close socket
     */
     public void closeSocket(){
         socket.close();
     }
    /*
     * sending string using send(byte[] message,int length)
     */

    public String send (String message)throws TimedOutException, IOException{
        byte[] data=message.getBytes();
        return send(data, data.length);
        
    }
    public String send (byte[] datagram, int length)throws TimedOutException, IOException{
        DatagramPacket packet;
  //     String stringMessage=new String(datagram);
        // String received = new String(packet.getData(),0,packet.getLength());
        
        //receiving response 
        byte[] buffer=new byte[2000];
     //   new DatagramPacket
        packet=new DatagramPacket(buffer, buffer.length);
        //we try maxRetries times to recive response
        //each retry got timeout/maxRetries miliseconds
        //after maxRetries retries we assume the server is dead
        socket.setSoTimeout(timeout/maxRetries);
        for (int i=0;i<maxRetries;i++)
        {
            //try to send
            try
            {//adding message id to the message
                String stringMessage=new String(datagram);
                String messageWithID=stringMessage+"@ID@"+String.valueOf(messageID);
                byte[] datagramWithMessage=messageWithID.getBytes();
                sendNoResponse(datagramWithMessage, datagramWithMessage.length);

                try
                {
                    socket.receive(packet);
                    String received = new String(packet.getData(),0,packet.getLength());
                    //parsing the ID of the message
                    int receivedID=Integer.parseInt(received.split("@ID@",2)[1]);
                    if (messageID==receivedID)
                        return received.split("@ID@")[0];//the message itself.
                }
                catch (SocketTimeoutException noResponse)
                {
                if (i==maxRetries-1)
                {
                    messageID++;
                    throw (new TimedOutException());       
                }
                }
                finally
                {
                    messageID++;
                }
            }
            catch (TimedOutException timedOut)
            {
                if (i==maxRetries-1)
                {
                    messageID++;
                    throw (new TimedOutException());       
                }
            }
            catch (IOException e)
            {
                if (i==maxRetries-1)
                {
                    messageID++;
                    throw (new TimedOutException());       
                }
            }
            finally
            {
                messageID++;
            }
        
        }
        return "";
    }
    
    /*
     * the mediator just send his response
     * without waiting for another response
     */
    protected void sendNoResponse (String message) throws TimedOutException, IOException {
        byte[] data=message.getBytes();
        sendNoResponse(data, data.length);
    }
    
    /*
     * the mediator just send his response
     * without waiting for another response
     */
    protected void sendNoResponse (byte[] datagram,int length) throws TimedOutException, IOException {
        DatagramPacket packet =
            new DatagramPacket(datagram, datagram.length, address.getAddress(), port);
	socket.send(packet);
    }
    
    /*
     * recive datagram packet and return it.
     */
    public DatagramPacket recive() throws IOException{
        byte[] buffer=new byte[2000];
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length) ;
	socket.receive(packet);
	return packet;     
    }
}
